use std::sync::Arc;

use async_trait::async_trait;
use common::compose::{CallBox, CallCore, ContextBox, FError};
use rig::{
    agent::Agent,
    completion::{CompletionModel, Prompt},
    providers::{openai, deepseek, gemini},
};

/// 提供商枚举，支持不同的 AI 服务提供商
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Provider {
    /// DeepSeek AI
    DeepSeek,
    /// OpenAI
    OpenAI,
    /// Google Gemini
    Gemini,
}

impl Provider {
    /// 获取提供商的环境变量名称
    pub fn env_var_name(&self) -> &'static str {
        match self {
            Provider::DeepSeek => "DEEPSEEK_API_KEY",
            Provider::OpenAI => "OPENAI_API_KEY",
            Provider::Gemini => "GEMINI_API_KEY",
        }
    }

    /// 获取提供商的默认模型
    pub fn default_model(&self) -> &'static str {
        match self {
            Provider::DeepSeek => deepseek::DEEPSEEK_CHAT,
            Provider::OpenAI => "gpt-3.5-turbo",
            Provider::Gemini => gemini::completion::GEMINI_1_5_PRO,
        }
    }

    /// 从字符串解析提供商
    pub fn from_str(s: &str) -> Result<Self, String> {
        match s.to_lowercase().as_str() {
            "deepseek" => Ok(Provider::DeepSeek),
            "openai" => Ok(Provider::OpenAI),
            "gemini" => Ok(Provider::Gemini),
            _ => Err(format!("Unsupported provider: {}", s)),
        }
    }
}

/// 提供商特定的配置选项
#[derive(Debug, Clone)]
pub enum ProviderConfig {
    /// DeepSeek 特定配置
    DeepSeek {
        /// API 密钥
        api_key: String,
        /// 温度参数，越高越创造，越低越精确
        temperature: f64,
        /// 最大输出标记数
        max_tokens: Option<u64>,
    },
    /// OpenAI 特定配置
    OpenAI {
        /// API 密钥
        api_key: String,
        /// 基础 URL，如果为 None 则使用环境变量或默认值
        base_url: Option<String>,
        /// 温度参数，越高越创造，越低越精确
        temperature: f64,
        /// 最大输出标记数
        max_tokens: Option<u64>,
    },
    /// Gemini 特定配置
    Gemini {
        /// API 密钥
        api_key: String,
        /// 基础 URL，如果为 None 则使用环境变量或默认值
        base_url: Option<String>,
        /// 温度参数，越高越创造，越低越精确
        temperature: f64,
        /// 最大输出标记数
        max_tokens: Option<u64>,
    },
}

impl ProviderConfig {
    /// 从环境变量中创建提供商配置
    pub fn from_env(provider: Provider, temperature: f64, max_tokens: Option<u64>) -> Result<Self, String> {
        use std::env;

        let get_api_key = |env_var: &str| -> Result<String, String> {
            env::var(env_var).map_err(|e| format!("环境变量错误: {}", e))
        };

        match provider {
            Provider::DeepSeek => {
                let api_key = get_api_key(provider.env_var_name())?;
                Ok(Self::DeepSeek {
                    api_key,
                    temperature,
                    max_tokens,
                })
            },
            Provider::OpenAI => {
                let api_key = get_api_key(provider.env_var_name())?;
                let base_url = env::var("OPENAI_BASE_URL").ok();
                Ok(Self::OpenAI {
                    api_key,
                    base_url,
                    temperature,
                    max_tokens,
                })
            },
            Provider::Gemini => {
                let api_key = get_api_key(provider.env_var_name())?;
                let base_url = env::var("GEMINI_BASE_URL").ok();
                Ok(Self::Gemini {
                    api_key,
                    base_url,
                    temperature,
                    max_tokens,
                })
            },
        }
    }
}

/// 通用 AI 代理配置选项
#[derive(Debug, Clone)]
pub struct AgentConfig {
    /// 温度参数，越高越创造，越低越精确
    pub temperature: f64,
    /// 最大输出标记数
    pub max_tokens: Option<u64>,
    /// 是否启用工具调用
    pub enable_tools: bool,
    /// 提供商特定的配置
    pub provider_config: Option<ProviderConfig>,
}

impl Default for AgentConfig {
    fn default() -> Self {
        Self {
            temperature: 0.3,
            max_tokens: None,
            enable_tools: false,
            provider_config: None,
        }
    }
}

/// 通用的 AI 代理结构体，封装了不同的 AI 模型
struct GenericAgent<M: CompletionModel> {
    agent: Agent<M>,
}

#[async_trait]
impl<M: CompletionModel + Send + Sync + 'static> CallCore<String, String> for GenericAgent<M> {
    async fn run(&self, prompt_text: String, _: ContextBox) -> Result<String, FError> {
        Prompt::prompt(&self.agent, prompt_text)
            .await
            .map_err(|e| FError::Generic(Box::new(std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))))
    }
}

/// 创建通用 AI 代理，可以根据传入的 system_prompt 执行各种任务
///
/// # 参数
///
/// * `provider_config` - 提供商配置，包含提供商类型和API密钥
/// * `model` - 模型名称，如果为空则使用默认模型
/// * `system_prompt` - 系统提示词，定义代理的行为和能力
///
/// # 示例
///
/// ```rust,no_run
/// use agent::generic::{create_agent, Provider, ProviderConfig};
/// use common::compose::make_empty_ctx;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     // 创建一个翻译代理
///     let translator = create_agent(
///         ProviderConfig::OpenAI {
///             api_key: "your-api-key".to_string(),
///             base_url: None,
///             temperature: 0.3,
///             max_tokens: None,
///         },
///         "gpt-4",
///         "你是一位专业的中文翻译。请将以下文本翻译成中文，保持原文的意思、风格和格式："
///     );
///
///     // 使用代理进行翻译
///     let result = translator.run("Hello, world!".to_string(), make_empty_ctx()).await?;
///     println!("{}", result);
///
///     // 创建一个代码审查代理
///     let code_reviewer = create_agent(
///         ProviderConfig::DeepSeek {
///             api_key: "your-api-key".to_string(),
///             temperature: 0.2,
///             max_tokens: Some(2000),
///         },
///         "",
///         "你是一位经验丰富的代码审查专家。请审查以下代码，指出潜在的问题、改进建议和最佳实践："
///     );
///
///     // 使用代理进行代码审查
///     let code = "fn main() { let x = 5; println!(\"{}\", x); }";
///     let review = code_reviewer.run(code.to_string(), make_empty_ctx()).await?;
///     println!("{}", review);
///
///     Ok(())
/// }
/// ```
pub fn create_agent(
    provider_config: ProviderConfig,
    model: &str,
    system_prompt: &str,
) -> CallBox<'static, String, String> {
    // 获取模型名称，如果为空则使用默认模型
    let model_name = match &provider_config {
        ProviderConfig::DeepSeek { .. } => {
            if model.is_empty() { Provider::DeepSeek.default_model() } else { model }
        },
        ProviderConfig::OpenAI { .. } => {
            if model.is_empty() { Provider::OpenAI.default_model() } else { model }
        },
        ProviderConfig::Gemini { .. } => {
            if model.is_empty() { Provider::Gemini.default_model() } else { model }
        },
    };

    match provider_config {
        ProviderConfig::DeepSeek { api_key, temperature, max_tokens } => {
            // 创建 DeepSeek client 和 agent
            let client = deepseek::Client::new(&api_key);
            let mut agent_builder = client.agent(model_name)
                .preamble(system_prompt)
                .temperature(temperature);

            if let Some(max_tokens) = max_tokens {
                agent_builder = agent_builder.max_tokens(max_tokens);
            }

            let agent = agent_builder.build();
            let generic_agent = GenericAgent { agent };
            Arc::new(Box::new(generic_agent))
        },
        ProviderConfig::OpenAI { api_key, base_url, temperature, max_tokens } => {
            // 创建 OpenAI client 和 agent
            let client = if let Some(url) = base_url {
                // 使用自定义的 base_url
                openai::Client::from_url(&api_key, &url)
            } else {
                // 尝试从环境变量中获取 base_url，如果没有则使用默认值
                match std::env::var("OPENAI_BASE_URL") {
                    Ok(base_url) => openai::Client::from_url(&api_key, &base_url),
                    Err(_) => openai::Client::new(&api_key),
                }
            };

            let mut agent_builder = client.agent(model_name)
                .preamble(system_prompt)
                .temperature(temperature);

            if let Some(max_tokens) = max_tokens {
                agent_builder = agent_builder.max_tokens(max_tokens);
            }

            let agent = agent_builder.build();
            let generic_agent = GenericAgent { agent };
            Arc::new(Box::new(generic_agent))
        },
        ProviderConfig::Gemini { api_key, base_url, temperature, max_tokens } => {
            // 创建 Gemini client 和 agent
            let client = if let Some(url) = base_url {
                // 使用自定义的 base_url
                gemini::Client::from_url(&api_key, &url)
            } else {
                // 尝试从环境变量中获取 base_url，如果没有则使用默认值
                match std::env::var("GEMINI_BASE_URL") {
                    Ok(base_url) => gemini::Client::from_url(&api_key, &base_url),
                    Err(_) => gemini::Client::new(&api_key),
                }
            };

            let mut agent_builder = client.agent(model_name)
                .preamble(system_prompt)
                .temperature(temperature);

            if let Some(max_tokens) = max_tokens {
                agent_builder = agent_builder.max_tokens(max_tokens);
            }

            let agent = agent_builder.build();
            let generic_agent = GenericAgent { agent };
            Arc::new(Box::new(generic_agent))
        },
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use common::compose::make_empty_ctx;
    use dotenvy::dotenv;

    /// 测试辅助函数，创建代理并测试
    async fn test_agent(provider: Provider) -> Result<(), String> {
        // 如果没有设置环境变量，则跳过测试
        let env_var = provider.env_var_name();
        if std::env::var(env_var).is_err() {
            return Err(format!("跳过测试：未设置 {} 环境变量", env_var));
        }

        // 从环境变量创建提供商配置
        let provider_config = ProviderConfig::from_env(provider, 0.2, Some(100))?;

        let system_prompt = "你是一位专业的助手，请简洁地回答问题。";
        let agent = create_agent(provider_config, "", system_prompt);

        let input = "你好，请介绍一下你自己。";
        let result = agent.run(input.to_string(), make_empty_ctx())
            .await
            .map_err(|e| format!("运行失败: {}", e))?;

        println!("{:?} response: {}", provider, result);

        if result.is_empty() {
            return Err("响应为空".to_string());
        }

        Ok(())
    }

    #[tokio::test]
    async fn test_deepseek_agent() {
        dotenv().ok();

        match test_agent(Provider::DeepSeek).await {
            Ok(_) => {},
            Err(e) => println!("{}", e),
        }
    }

    #[tokio::test]
    async fn test_openai_agent() {
        dotenv().ok();

        match test_agent(Provider::OpenAI).await {
            Ok(_) => {},
            Err(e) => println!("{}", e),
        }
    }

    #[tokio::test]
    async fn test_gemini_agent() {
        dotenv().ok();

        match test_agent(Provider::Gemini).await {
            Ok(_) => {},
            Err(e) => println!("{}", e),
        }
    }

    #[test]
    fn test_provider() {
        // 测试环境变量名称
        assert_eq!(Provider::DeepSeek.env_var_name(), "DEEPSEEK_API_KEY");
        assert_eq!(Provider::OpenAI.env_var_name(), "OPENAI_API_KEY");
        assert_eq!(Provider::Gemini.env_var_name(), "GEMINI_API_KEY");

        // 测试默认模型
        assert_eq!(Provider::DeepSeek.default_model(), deepseek::DEEPSEEK_CHAT);
        assert_eq!(Provider::OpenAI.default_model(), "gpt-3.5-turbo");
        assert_eq!(Provider::Gemini.default_model(), gemini::completion::GEMINI_1_5_PRO);

        // 测试从字符串解析
        assert_eq!(Provider::from_str("deepseek").unwrap(), Provider::DeepSeek);
        assert_eq!(Provider::from_str("openai").unwrap(), Provider::OpenAI);
        assert_eq!(Provider::from_str("gemini").unwrap(), Provider::Gemini);
        assert_eq!(Provider::from_str("DEEPSEEK").unwrap(), Provider::DeepSeek); // 测试大小写不敏感
        assert!(Provider::from_str("unknown").is_err());
    }
}
